# Create your views here.
import math
import networkx as nx

from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db import connection

from ITInfluence.models import Tweet, User
from ITInfluence.models import InfrarougeUser, InfrarougeForum, InfrarougeForumThread, InfrarougeThreadMessage

from ITInfluence import twitter, followers, NDI
from ITInfluence.plot import GraphPlotter, TwoDimensionalValuesPlotter
from ITInfluence.hashtags import getAllHashtags, getSimilarHashtags
from ITInfluence.infrarouge import InfrarougeGraphBuilder
from ITInfluence.TwitterGraphBuilder import TwitterGraphBuilder, TwitterFollowersGraphBuilder

from Tools.Timer import timer

from SentiStrength import sentistrength
from InfrarougeTwitterInfluence.settings import DATABASES


'''VIEWS'''
def index(request):
    return redirect("/infrarouge/")

def twitterStats(request):
    NDIPath = "/twitter/images/ndi/"
    NDITimePath = "/twitter/images/ndi-time/"
    NDIRepliesCountPath = "/twitter/images/ndi-replies-count/"
    repliesGraphVisualizationPath = "/twitter/images/replies-graph/"
    followersGraphVisualizationPath = "/twitter/images/followers-graph/"
    
    tweetCount = Tweet.objects.count()
    
    ### Use one of the two methods (slow but exact count or fast estimation)
    ## METHOD 1
    # Uncomment for exact count (WARNING: slow for large tables)
#    userCount = User.objects.count()
#    friendshipCount = Friendship.objects.count()

    ## METHOD 2
    # Uncomment for fast count using approximation
    databaseName = DATABASES["default"]["NAME"]
    userTableName = "ITInfluence_user"
    friendshipTableName = "ITInfluence_friendship"
    query = """
SELECT table_name, table_rows
FROM information_schema.tables
WHERE table_schema = %s
"""

    cursor = connection.cursor()
    cursor.execute(query, [databaseName])
    response = cursor.fetchall()
    
    userCount = 0
    friendshipCount = 0
    
    for row in response:
        if row[0].lower() == userTableName.lower():
            userCount = row[1]
        if row[0].lower() == friendshipTableName.lower():
            friendshipCount = row[1]
                        
    cursor.close()
    ### END
    
    context = {
               "tweetCount": tweetCount,
               "userCount": userCount,
               "friendshipCount": friendshipCount,
               'NDIPath': NDIPath,
               'NDITimePath': NDITimePath,
               'NDIRepliesCountPath': NDIRepliesCountPath,
               "repliesGraphVisualizationPath": repliesGraphVisualizationPath,
               "followersGraphVisualizationPath": followersGraphVisualizationPath,
               }
    return render_to_response("ITInfluence/twitter.html", context, context_instance=RequestContext(request))

def twitterBrowseHashtags(request):
    hashtags = getAllHashtags()
    minimumOccurences = 10
    hashtags = [(tag, occurences, fontSize(occurences)) for (tag, occurences) in hashtags if occurences >= minimumOccurences]
    
    context = {
               "hashtags": hashtags,
               "minimumOccurences": minimumOccurences,
               }
    return render_to_response("ITInfluence/twitter-browse-hashtags.html", context, context_instance=RequestContext(request))

def twitterHashtag(request, hashtag=""):
    similarHashtags = getSimilarHashtags(hashtag)
    similarHashtags = [(tag, occurences, fontSize(occurences)) for (tag, occurences) in similarHashtags]
    
    context = {
               "hashtag": hashtag,
               "similarHashtags": similarHashtags,
               }
    return render_to_response("ITInfluence/hashtag.html", context, context_instance=RequestContext(request))

def twitterBrowseTweets(request):
    classifyTweets() # using sentistrength
    
    try:
        tweetsPerPage = int(request.GET.get("tpp"))
        if tweetsPerPage <= 0:
            raise Exception("Tweets per page can't be negative")
    except:
        tweetsPerPage = 20
        
    tweets = Tweet.objects.all().order_by("-created_at")
    paginator = Paginator(tweets, tweetsPerPage)
    
    tweetCount = Tweet.objects.count()
    
    page = request.GET.get("page")
    try:
        tweets = paginator.page(page)
    except PageNotAnInteger:
        tweets = paginator.page(1)
    except EmptyPage:
        tweets = paginator.page(paginator.num_pages)
    
    context = {
               "tweets": tweets,
               "tweetCount": tweetCount,
               "tweetsPerPage": tweetsPerPage,
               }
    return render_to_response("ITInfluence/twitter-browse-tweets.html", context, context_instance=RequestContext(request))

def twitterFollowersCountRanking(request):
    users = User.objects.all().filter(user_ready=1).order_by("-followers_count")
    
    rankingLength = 30 # top n users
    users = users[:rankingLength]
    
    context = {
               "users": users,
               }
        
    return render_to_response("ITInfluence/twitter-followers-ranking.html", context, context_instance=RequestContext(request))

def twitterStartCollectingStream(request, hashtag):
    context = {
               
               }
    return render_to_response("ITInfluence/twitter-collect-stream.html", context, context_instance=RequestContext(request))

def twitterShowCollectForm(request):
    totalTimeToCollectFolowers = followers.getTotalTimeToCollectFollowers()
    remainingTimeToCollectFolowers = followers.getRemainingTimeToCollectFollowers()
    
    twoDecimalFormat = "{0:.2f}"
    
    followersTimeDays = twoDecimalFormat.format(totalTimeToCollectFolowers[0])
    followersTimeHours = twoDecimalFormat.format(totalTimeToCollectFolowers[1])
    followersTimeMinutes = twoDecimalFormat.format(totalTimeToCollectFolowers[2])
    followersRemainingTimeDays = twoDecimalFormat.format(remainingTimeToCollectFolowers[0])
    followersRemainingTimeHours = twoDecimalFormat.format(remainingTimeToCollectFolowers[1])
    followersRemainingTimeMinutes = twoDecimalFormat.format(remainingTimeToCollectFolowers[2])
    
    try:
        followersProgressPercentage = 100 - 100 * (remainingTimeToCollectFolowers[2] / totalTimeToCollectFolowers[2])
    except:
        followersProgressPercentage = 100
    followersProgressPercentage = twoDecimalFormat.format(followersProgressPercentage)

    context = {
               "isCollectingFollowers": followers.rest.isCollectingFollowers,
               "followersTimeDays": followersTimeDays,
               "followersTimeHours": followersTimeHours,
               "followersTimeMinutes": followersTimeMinutes,
               "followersRemainingTimeDays": followersRemainingTimeDays,
               "followersRemainingTimeHours": followersRemainingTimeHours,
               "followersRemainingTimeMinutes": followersRemainingTimeMinutes,
               "followersProgressPercentage": followersProgressPercentage,
               }
    
    try:
        hashtagSeparator = ","
        # add a leading # to all tags seperated by commas
        hashtags = ["#" + tag.strip() for tag in request.POST["hashtag"].split(hashtagSeparator)]

        filterTag = hashtagSeparator.join(hashtags)
        
        stopTweetCount = int(request.POST["stopTweetCount"])
        stopTime = int(request.POST["stopTime"])
        
        # start collecting tweets
        twitter.startStreaming(filterHashtag=filterTag, stopTweetCount=stopTweetCount, stopTime=stopTime)
    except:
        pass
    
    context["isStreaming"] = twitter.stream.connected
    context["streamingHashtag"] = twitter.streamingHashtag
    return render_to_response("ITInfluence/twitter-collect-form.html", context, context_instance=RequestContext(request))

def twitterToggleCollectingFollowers(request):
    followers.toggleFollowersCollection()
    
    return redirect("/twitter/collect/")

def infrarougeStats(request):      
    userCount = InfrarougeUser.objects.count()
    forumCount = InfrarougeForum.objects.count()
    forumThreadCount = InfrarougeForumThread.objects.count()
    threadMessageCount = InfrarougeThreadMessage.objects.count()
    
    NDIPath = "/infrarouge/images/ndi/"
    NDITimePath = "/infrarouge/images/ndi-time/"
    NDIRepliesCountPath = "/infrarouge/images/ndi-replies-count/"
    
    repliesGraphVisualizationPath = "/infrarouge/images/replies-graph/"
    userDiscussionsGraphVisualizationPath = "/infrarouge/images/user-discussion-graph/"
    
    context = {
               'forumCount': forumCount,
               'forumThreadCount': forumThreadCount,
               'threadMessageCount': threadMessageCount,
               'userCount': userCount,
               'NDIPath': NDIPath,
               'NDITimePath': NDITimePath,
               'NDIRepliesCountPath': NDIRepliesCountPath,
               'repliesGraphVisualizationPath': repliesGraphVisualizationPath,
               'userDiscussionsGraphVisualizationPath': userDiscussionsGraphVisualizationPath,
               }
    return render_to_response("ITInfluence/infrarouge.html", context, context_instance=RequestContext(request))

def getInfrarougeForums(request):
    forums = InfrarougeForum.objects.all()
    
    context = {
               "forums": forums,
               }
    return render_to_response("ITInfluence/infrarouge-forums.html", context, context_instance=RequestContext(request))

def twitterStopStreaming(request):
    twitter.toggleStreaming()
    return redirect("/twitter/collect/")

'''Image views'''
def showImage(request, path):
    '''Wrapper view that displays the image at given path on a html page'''
    context ={
              "path": path,
              }
    return render_to_response("ITInfluence/show-image.html", context, context_instance=RequestContext(request))

# These views do NOT return HTML. They return PNG images.
def getInfrarougeNDI(request):
    with timer():
        resultsEqualTime, resultsEqualRepliesCount = NDI.polarizationForAllForums()
        
        plotter = TwoDimensionalValuesPlotter()
        xlabel = "Interval"
        ylabel = "NDI"
        response = plotter.memoryPlotMultipleDatasetsMultidimensional([resultsEqualTime, resultsEqualRepliesCount], xlabel, ylabel)
    
    return response

def getInfrarougeNDITimeFigure(request):
    with timer():
        resultsEqualTime, unused = NDI.polarizationForAllForums(equalRepliesCount=False)
        
        plotter = TwoDimensionalValuesPlotter()
        xlabel = "Interval (equal time)"
        ylabel = "NDI"
        response = plotter.memoryPlotMultipleDatasets(resultsEqualTime, xlabel, ylabel)
    
    return response

def getInfrarougeNDIReplyCountFigure(request):
    with timer():
        unused, resultsEqualRepliesCount = NDI.polarizationForAllForums(equalTime=False)
        
        plotter = TwoDimensionalValuesPlotter()
        xlabel = "Interval (equal reply count)"
        ylabel = "NDI"
        response = plotter.memoryPlotMultipleDatasets(resultsEqualRepliesCount, xlabel, ylabel)
    
    return response

def getInfrarougeRepliesGraph(request):
    with timer():
        infrarougeGraphBuilder = InfrarougeGraphBuilder(NDI.infrarougeDatabasePath)
        graphPlotter = GraphPlotter()
        graph = infrarougeGraphBuilder.graph1
        nodeLabel="User"
        edgeLabel="Reply"
        response = graphPlotter.memoryPlot(graph, nodeLabel=nodeLabel, edgeLabel=edgeLabel)
    
    return response

def getInfrarougeUserDiscussionGraph(request):
    with timer():
        infrarougeGraphBuilder = InfrarougeGraphBuilder(NDI.infrarougeDatabasePath)
        graphPlotter = GraphPlotter()
        graph = infrarougeGraphBuilder.graph2
        # compute bipartite positions for nodes
        pos = graphPlotter.bipartiteNodePositions(graph)
        nodeLabel="User"
        nodeLabel2="Discussion"
        edgeLabel="Participation"
        response = graphPlotter.memoryPlot(graph, bipartite=True, pos=pos, nodeLabel=nodeLabel, nodeLabel2=nodeLabel2, edgeLabel=edgeLabel)
    
    return response

def getTwitterNDI(request):
    with timer(): # measure execution time
        resultsEqualTime, resultsEqualRepliesCount = NDI.getNDIForMostFrequentHashtags()
        
        plotter = TwoDimensionalValuesPlotter()
        xlabel = "Interval"
        ylabel = "NDI"
        response = plotter.memoryPlotMultipleDatasetsMultidimensional([resultsEqualTime, resultsEqualRepliesCount], xlabel, ylabel)
    
    return response

def getTwitterNDISingleHashtag(request, hashtag):
    with timer():
        resultsEqualTime, resultsEqualRepliesCount = NDI.getNDIForHashtag(hashtag)
        
        plotter = TwoDimensionalValuesPlotter(width=800, height=600)
        xlabel = "Interval"
        ylabel = "NDI"
        response = plotter.memoryPlotMultipleDatasetsMultidimensional([resultsEqualTime, resultsEqualRepliesCount], xlabel, ylabel)
    
    return response

def getTwitterNDITimeFigure(request):
    with timer():
        resultsEqualTime, unused = NDI.getNDIForMostFrequentHashtags(equalRepliesCount=False)
        
        plotter = TwoDimensionalValuesPlotter()
        xlabel = "Interval (equal time)"
        ylabel = "NDI"
        response = plotter.memoryPlotMultipleDatasets(resultsEqualTime, xlabel, ylabel)
    
    return response

def getTwitterNDIReplyCountFigure(request):
    with timer():
        unused, resultsEqualRepliesCount = NDI.getNDIForMostFrequentHashtags(equalTime=False)
        
        plotter = TwoDimensionalValuesPlotter()
        xlabel = "Interval (equal time)"
        ylabel = "NDI"
        response = plotter.memoryPlotMultipleDatasets(resultsEqualRepliesCount, xlabel, ylabel)
    
    return response

def getTwitterRepliesGraph(request):
    with timer():
        twitterGraphBuilder = TwitterGraphBuilder()
        graph = twitterGraphBuilder.graph
        
        # save graph to gml file
#         filename = "replies_graph.gml"
#         saveGraph(graph, filename)
        
        graphPlotter = GraphPlotter()
        print("Plotting graph...")
        response = graphPlotter.memoryPlot(graph)
        print("...done!")
    
    return response

def getTwitterFollowersGraph(request):
    with timer():
        twitterGraphBuilder = TwitterFollowersGraphBuilder()
        graph = twitterGraphBuilder.graph
        
        # save graph to gml file
#         filename = "followers_graph.gml"
#         saveGraph(graph, filename)
        
        graphPlotter = GraphPlotter()
        nodeLabel="User"
        edgeLabel="Friendship"
        print("Plotting graph...")
        response = graphPlotter.memoryPlot(graph, nodeSizes=twitterGraphBuilder.nodeSizes, nodeColor=twitterGraphBuilder.nodeSizes, nodeLabel=nodeLabel, edgeLabel=edgeLabel)
        print("...done!")
    
    return response

'''HELPER FUNCTIONS'''
def saveGraph(graph, filename):
    nx.gml.write_gml(graph, filename)

def fontSize(occurences):
    return 1 + math.log(occurences)

def classifyTweets():
    # only classify tweets that are not classified yet
    tweets = Tweet.objects.all().filter(polarity_ready=False)
    
    if len(tweets) > 0:
        s = sentistrength.SentiStrength()
        # concatenate all texts to annotate at once
        text = ""
        for tweet in tweets:
            text += tweet.text.replace("\n", " ") + "\n"
        
        polarities = s.classifiy(text)
        # update tweets with their polarity
        for tweet, polarity in zip(tweets, polarities):
            tweet.polarity = polarity
            tweet.polarity_ready = True
            tweet.save()